﻿using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.Threading;
using System.Windows;

namespace WiMo.Games.GameEngine.WP7S
{
    public class GameEngine : Canvas, IGameEngine
    {
        public GameEngine()
        {

            AssetFactory = new AssetFactory(this);
            ContentManager = new ContentManager(this);
            Components = new System.Collections.Generic.List<IGameComponent>();
            GameTime = new GameTime();
            Randomizer = new Random(DateTime.Now.Millisecond);
        }

        private bool _running = true;
        private bool _active = true;

        public bool EnableDiagnostics { get; set; }
        public bool IsPaused { get; set; }
        public Color DiagnosticsFontColor { get; set; }
        public PointF  DiagnosticsLocation { get; set; }
        public ISpriteFont DefaultFont { get; set; }
        public IDisplay Display { get; set; }
        public IAssetFactory AssetFactory { get; set; }
        public IGame Game { get; private set; }
        public object GraphicsManager { get; private set; }
        public IAudioManager AudioManager { get; set; }
        public WiMo.Games.Inputs.IInputManager InputManager { get; set; }
        public IContentManager ContentManager { get; set; }
        public ICollisionManager CollisionManager { get; set; }
        public IPhysicsManager PhysicsManager { get; set; }
        public IStateManager StateManager { get; set; }
        public WiMo.Games.Menus.IMenuManager MenuManager { get; set; }
        public System.Collections.Generic.List<IGameComponent> Components { get; set; }

        public void Initialize(IGame game)
        {
            Game = game;
        }

        public void Start()
        {
            var frameCount = 0;
            var renderMs = 0;
            var collisionMs = 0;

            DiagnosticsLocation = new PointF(120, 20);

            Game.CreateMenus();
            Game.LoadContent();

            foreach (var component in Components)
                component.LoadContent(ContentManager);

            Game.Initialize();
            foreach (var component in Components)
                component.Initialize();

            var start = DateTime.Now;

            new Thread(() =>
            {
                Game.BeginRun();
                long renderMS = 0;
                int fps = 0;
                bool rendering = false;
                var fpsTimer = Stopwatch.StartNew();

                while (_running)
                {
                    if (EnableDiagnostics)
                        frameCount++;

                    rendering = true;
                    this.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        var timer = Stopwatch.StartNew();
                        var now = DateTime.Now;
                        int elapsedMS = (now - start).Milliseconds;
                        GameTime.Update(elapsedMS);
                        start = now;

                        if (!IsPaused)
                        {                            
                            PhysicsManager.Update(GameTime);
                            CollisionManager.Update(GameTime);
                            collisionMs = (int)timer.ElapsedMilliseconds;
                        }

                        Game.Update(GameTime);
                        foreach (var component in Components)
                            component.Update(GameTime);
                        Display.Reset();

                        Game.Draw(GameTime);

                        foreach (var component in Components)
                            component.Draw(GameTime);

                        InputManager.Draw(GameTime);
                        InputManager.Update();

                        if (EnableDiagnostics)
                        {
                            if (fpsTimer.ElapsedMilliseconds > 1000)
                            {
                                fps = frameCount;
                                frameCount = 0;
                                fpsTimer.Reset();
                                fpsTimer.Start();
                            }

                            string debugMessage = string.Format("FPS: {0:00} R: {1:000} C: {2:000}", fps, renderMs, collisionMs);
                            Display.DrawText(DiagnosticsLocation, debugMessage, DefaultFont, DiagnosticsFontColor, WiMo.Games.Drawables.Origin.TopLeft);
                        }

                        timer.Reset();
                        timer.Start();
                        Display.Render();
                        renderMS = timer.ElapsedMilliseconds;
                        rendering = false;
                    }));

                    var loopTime = 0;
                    while (rendering)
                    {
                        System.Threading.Thread.Sleep(1);
                        loopTime++;
                    }

                    loopTime = Math.Min(loopTime, 16);
                                    
                    System.Threading.Thread.Sleep(33 - loopTime);
                }                
              
            }).Start();
        }

        public void StartHopper()
        {

        }

        public void StopHopper()
        {

        }

        public GameTime GameTime
        {
            get;
            set;
        }

        public new void Exit()
        {
            _running = false;
        }

        public void Pause()
        {
            IsPaused = true;
        }

        public void Resume()
        {
            IsPaused = false;
        }

        public void RegisterComponent(IGameComponent component)
        {
            component.InputManager = InputManager;
            component.Display = Display;

            Components.Add(component);
        }

        public void OnResize(Size bounds)
        {

        }

        public Random Randomizer { get; private set; }

        public void Vibrate(int ms)
        {

        }

        public void BeginVibrate()
        {

        }

        public void EndVibrate()
        {

        }
    }
}
